home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
pmode
/
pmode251
/
pmode.doc
< prev
next >
Wrap
Text File
|
1994-11-29
|
31KB
|
656 lines
Greetings... This is the documentation for PMODE v2.51 assembly protected
mode header by Tran (a.k.a. Thomas Pytel). It is intended for assembly
programmers with a good deal of knowlege of the 386. In this doc is explained
the workings and usage of PMODE.ASM. This doc was originally written for a
much earlier version of PMODE, but it is suitable (with minor adjustments) for
PMODE v2.51.
------------------------------------------------------------------------------
Contents:
---------
0 - Overview
0.0 - What it does
0.1 - Your code
1 - Memory, segments, and selectors
1.0 - Memory structure
1.1 - Usage and DOS
1.2 - Selectors
2 - The stack and calling across modes
2.0 - The stack
2.1 - Calling real mode
2.2 - Calling protected mode
3 - IRQs and exceptions
3.0 - IRQs
3.1 - DPMI and the stack
3.2 - Chaining to real mode
3.3 - The IF flag
3.4 - Exceptions
4 - Functions and data
4.0 - Variables
4.1 - Memory functions
4.2 - IRQ functions
4.3 - Selector functions
4.4 - Other functions
5 - Notes
5.0 - Updates
5.1 - DMA problems
5.2 - Misc notes
5.3 - Final word
------------------------------------------------------------------------------
0 - Overview:
-------------
PMODE.ASM is a small piece of assembly code which is intended to allow for
easy 32bit flat protected mode coding in assembly. I wrote it for myself, and
it was designed for exactly what I need. But since it is so useful, and there
are not too many other alternatives for protected mode ASM coding, I am
putting it out for public distribution. The current version of PMODE has
been evolving for a while. There has been plenty of time to refine and debug
it. It is at this point, from my own and friends experiences, totally stable
and bug-free. I do not make this statement lightly. But I do not guarantee it
either.
PMODE v2.51 is a touched up version of PMODE v2.4. Most (I think all) of the
dead code has been removed from previous versions of PMODE. One minor DPMI bug
has been fixed and the DPMI memory block allocation had been changed to
compensate for a QDPMI bug (QEMM DPMI host). The previous allocation scheme
may also have been the cause of memory allocation failure under OS/2. PMODE is
a dedicated ASM extender. So far it has proven very stable and pretty clean
over all. If you have been looking for something like this, feel free to use
it. Or examine the source code if you just want to learn (though the code is a
bit messy). All I ask if you use this code is credits.
0.0 - What it does:
-------------------
PMODE provides a flat 32bit protected mode environment for assembly code to
run in. No matter what kind of 386 protection control is already in place. It
will run under VCPI, DPMI, or in raw mode with no 386 control system in place.
This includes all the popular memory managers, Windows the virus, OS/2, etc...
PMODE will take care of detecting a 386+ processor, making sure there is
enough memory and allocating it. It will handle all the minor details of 386
protected mode. Send IRQs that occur in protected mode to their real mode
handlers, or allow you to intercept them. And, ofcourse, allow your protected
mode code to call real mode interrupts and routines. PMODE will also take care
of allocating and maintaining the stack across mode calls.
When a PMODE program starts up, PMODE will do all of its necessary starup
tasks. Then, if no error was detected (not enough mem, no 386+, etc...), it
will jump to a label in your code called _main. Your program takes over from
there with all interrupts disabled since real mode. You must do a STI to
enable them. Don't forget this like I have so many times. When your program is
done and wants to exit to DOS, simply jump to a label called _exit, defined
in PMODE. You can pass back an error code to DOS. There is a variable called
_exitcode which contains the byte to pass back to DOS (AL in INT 21h AH=4c).
0.1 - Your code:
----------------
All your 32bit code goes into one large CODE32 segment. It runs in 32bit
protected mode from start to finish. All your code and data reside in this
one large segment. Though you can read or write stuff below the beginning of
CODE32. Within your code you will hardly ever have to worry about segment
overrides or stuff like that. Its just pretty much straight flat assembly with
full 32bit linear addresses for all your code and data. You can do most of the
stuff you do in real mode. I would not suggest trying to go TSR or running
external programs from your PMODE code though. It is doable, and will probably
work, but PMODE was not designed for that. You would have to free up low
memory yourself, and you can't free up high memory. There may be other minor
problems uncompensated for. PMODE is meant for standalone ASM programs like
games or demos, and does very well for that.
------------------------------------------------------------------------------
1 - Memory, segments, and selectors:
------------------------------------
In protected mode, your program has access to all free low and high memory
that could be allocated. Low memory is all memory visible to DOS below 1M.
High memory is all extended memory that could be allocated through VCPI, DPMI,
XMS, or INT 15 AH=88h of raw mode. All this memory can be accessed directly
realative to the beginning of the 32bit protected mode segment (CODE32). You
can also access memory realative to the absolute beginning of memory. Three
main selectors are set up by PMODE. One for the code segment starting at
CODE32, its limit is set to 4G. One for a data segment alias for the code
segment (same memory space), also at 4G. And one for a data segment beginning
at the absolute beginning of memory. In fact, all selectors allocated by PMODE
are set to a limit of 4G.
1.0 - Memory structure:
-----------------------
The structure of memory when your code begins executing is as follows:
+-------------------------------------------------------------+
| Normal crap (real mode int vector table, DOS, TSRs, etc...) |
+-------------------------------------------------------------+
| PSP |
+-------------------------------------------------------------+
| Beginning of CODE16 - PMODE 16bit code and data |
+-------------------------------------------------------------+
| Beginning of CODE32 - PMODE 32bit code and data |
+-------------------------------------------------------------+
| Your 32bit code and data |
+-------------------------------------------------------------+
| The stack |
+-------------------------------------------------------------+
| PMODE data allocated at run-time (IDT, TSS, etc...) |
+-------------------------------------------------------------+
| Free low memory (_lomembase to _lomemtop) |
+-------------------------------------------------------------+
| Video buffer, ROMS, also possibly hiram |
+-------------------------------------------------------------+
| Used extended memory (if any) |
+-------------------------------------------------------------+
| Free extended memory (if any, _himembase to _himemtop) |
+-------------------------------------------------------------+
| Used extended memory (if any) |
+-------------------------------------------------------------+
When your code begins execution at _main, you can be sure there is a minimum
of low memory available. This number is specified at the top of PMODE.ASM as
LOWMIN. There is also a minimum of high memory specified in PMODE.ASM as
EXTMIN. The beginning and ending addresses of these memory spaces, realative
to the beginning of CODE32, are defined in four variables made available to
your code. _lomembase is the linear address of the beginning of free low
memory. _lomemtop is the linear address of the top of low memory (last
possible byte+1). _himembase is the beginning of extended memory, and
_himemtop is the end of it. All of these vars can be modified by your program.
They will also be modified by PMODE memory functions. See section 4 for a
description of these functions. Basically all that they do is see if there is
enough memory available for a block you request. If there is, they pass back
a pointer to the base of free memory of the type you requested, then adjust
that base by the amount you requested.
The functions (just tiny stubs of code really) provided for memory are just
straight linear functions. No management of blocks of memory is done. I
personally don't like fragmentations of memory, and can work fine without the
need for blocks. If you want, write yourself a little malloc library if that
is what you need.
1.1 - Usage and DOS:
--------------------
When you call real mode DOS interrupts, or any real mode procedure that
expects a buffer, you will have to make sure that buffer is in low memory.
Remember that real mode DOS and code can only see low memory. Your code and
data and the stack always reside in low memory. If you want to pass something
that is in high memory however, you will have to copy it to somewhere in low
memory. Once in low memory, you will have to get the real mode segment:offset
pair for your linear address. Figuring this out is very easy. All linear
addresses in PMODE are realative to the beginning of the CODE32 segment. To
convert to an absolute address, all you have to do is add the offset of the
CODE32 segment from absolute 0. And just this value is available in a variable
called _code32a. All this really is, is the real mode segment value of CODE32
shifted left 4 bits. So for example, _lomembase+_code32a is the linear address
of the beginning of free low memory from absolute 0. And if you don't know how
to convert this type of address to a seg:off pair, you should probably not be
reading this doc just yet.
1.2 - Selectors:
----------------
As you know (and if you don't, get some books and learn more about protected
mode), in protected mode the segment registers don't work with segment
addresses, but with selectors. Selectors are indexes into tables describing
segments. Appropriately enough these tables are called descriptor tables. And
you do not have to worry about them, PMODE takes care of all the crap
associated with them.
The three main selectors you need (the code, data, and zero selectors) are
set up by PMODE. Their numerical values are stored in three variables made
available to your program. _selcode, _seldata, and _selzero are these
variables. When your program begins execution at _main, CS is obviously set
to _selcode, DS, ES, FS, and SS to _seldata, and GS is set to _selzero. With
the exception of FS and SS, this is the state PMODE expects the segment regs
to be whenever you call one of its functions or interrupts.
PMODE also allows you to allocate extra data selectors and to set their base
addresses to anything you want. This could be useful for tight routines where
you want to conserve register space and use 16bit register addressing. You
can specify the maximum number of selectors you will need to have allocated
at any one time at the top of PMODE.ASM in the SELECTORS equate.
During the execution of your code, you can modify DS, ES, FS, and GS to
whatever selector you wish. Just remember to set the appropriate values for
DS, ES, and GS before calling and PMODE functions or interrupts. Throughout
the execution of your program you can assume SS to be _seldata. Except in the
special case of IRQ handlers, explained in section 3.
------------------------------------------------------------------------------
2 - The stack and calling across modes:
---------------------------------------
The stack and calling across modes are closely tied together. Multiple
nested calls from protected to real mode and back are supported. DPMI also
feels the need to complicate the situation a little in dealing with IRQs, but
more on that later. You do not have to worry about the stack in calling across
modes, PMODE will take care of it.
2.0 - The stack:
----------------
PMODE sets up and maintains the stack for your program. In fact, your code
should not attempt to set up its own stack. Both because its not necessary,
and because of some other stupid historical reasons that I have forgotten by
now. There are four equates at the top of PMODE.ASM that determine the final
size and allocation of the stack. STAKMAIN sets the size of your main program
stack. The stack that will be used in your main stream of execution. STAKRMODE
is the size of the stack that will be given to any real mode code that is
called from protected mode. STAKPMODE is the size of the stack given to any
protected mode routines called from real mode. MODENESTING is the maximum
number of nested cross-mode calls supported. The final size of the master
stack is determined from all of these variables, and it cannot exceed 64k.
2.1 - Calling real mode:
------------------------
From your protected mode code you can call real mode interrupts and routines
using protected mode interrupts set up by PMODE for this purpose. You pass
registers to the real mode interrupt or routine through the use of virtual
registers, which are just memory images of the values to be set for those
registers in real mode. There are virtual registers for EAX, EBX, ECX, EDX,
ESI, EDI, EBP, DS, ES, FS, and GS. The virtual registers for AL, AH, and AX
share the appropriate memory space within the virtual EAX reg. Notice that
there are no SS, ESP, CS, and EIP registers. CS:EIP is taken from the real
mode interrupt vektor table for interrupt calls, and passed in the real CX:DX
registers for a procedure call. SS:ESP is set up by PMODE.
An INT 32h instruction in protected mode calls a real mode procedure. The
real CX:DX registers are the seg:off of the real mode procedure. The real mode
procedure must return with a RETF. The interrupt enable flag is preserved
across the call to real mode, but not back. After the INT 32h the IF flag will
be the same as before. The real carry, zero, aux, parity, sign, and overflow
flags will be passed back from the real mode routine unchanged. The real CPU
registers will be set to the values of the virtual registers for the real mode
routine. The return values of the routine will be stored back into the virtual
registers. The actual CPU registers will be unchanged in protected mode.
An INT 33h in protected mode will call a real mode interrupt handler. AL is
the interrupt number you want to call. The interrupt flag is disabled for the
real mode interrupt handler just as it is in real mode. Other than this, INT
33h works just like INT 32h with respect to virtual registers and the real
flags passed back from the handler.
One minor thing I must point out. INT 32h and INT 33h do not preserve FS. It
is returned as _seldata.
2.2 - Calling protected mode:
-----------------------------
You can also call a protected mode routine from real mode. The virtual
registers are used again to pass register values. With the exception of the
segment registers. For the protected mode routine, CS will obviously be
_selcode. DS, ES, FS, and SS will be _seldata. GS will be _selzero. Also, the
direction flag will be cleared for the protected mode routine. The virtual
DS, ES, FS, and GS registers are untouched by a call to a protected mode
routine. The interrupt enable flag is preserved across the call, but not back,
just like a protected mode call to INT 32h. The carry, zero, aux, parity,
sign, and overflow flags are also passed back real mode from the protected
mode routine. To call a protected mode routine from real mode, do an INT 32h
in real mode. That's right, another INT 32h. But its a different handler from
the protected mode version of the int. The offset of the protected mode
routine you want to call should be in EDX. And that routine must return with
a regular RET.
------------------------------------------------------------------------------
3 - IRQs and exceptions:
------------------------
First thing, let me just say that many DPMI drivers are very buggy in the
area of IRQs. They may not reflect them properly to real mode. Or they may
screw up if you try to do a call to real mode from a protected mode IRQ
handler. Keep this is mind if you are testing your code under a DPMI system.
3.0 - IRQs:
-----------
By default, IRQs that occur in protected mode are sent on to their real mode
handlers (this counts as a mode switch to the PMODE master stack). PMODE
allows you to install your own protected mode IRQ handlers. There are two
functions for getting and setting protected mode IRQ vectors in PMODE (see
section 4). Once set with these functions, your protected mode IRQ handler
will be active, but only in protected mode. If an IRQ you have hooked occurs
while processing a call to real mode (INT 32h or 33h or an IRQ that is being
processed by its real mode handler), it will go to the handler specified in
the real mode interrupt vector table. PMODE provides another set of functions
to create real mode callbacks to protected mode IRQ handlers, so that your
handler will get control no matter where the IRQ occurred. These callbacks,
however, modify the real mode interrupt vector table. Making chaining to the
real mode handler not as easy as using INT 33h with the appropriate interrupt
number.
You should terminate any protected mode IRQs with an IRETD (notice the D).
DPMI dox also say that this may not restore the interrupt enable flag, so
you should do a STI just before. However, all DPMIs I have tested seem to
restore the flag correctly without the STI.
3.1 - DPMI, and the stack:
--------------------------
The one anomaly to PMODEs stack with IRQs is DPMI. It sees fit to switch
onto its own stack whenever an IRQ goes off. And if you try to switch off that
stack, you will be severely punished by DPMI. For this reason, calls to real
mode (INT 32h, 33h) from IRQs under DPMI may or may not work depending on what
DPMI you're running under and how buggy it is. I have had no problems with
Windows DPMI driver in these situations, but you have been warned. Because
DPMI switches onto its own stack, you can not assume anything about SS in IRQ
handlers, and should definately not mess with it.
3.2 - Chaining to real mode:
----------------------------
If you don't set the real mode IRQ callback for a particular IRQ, chaining
to that IRQs real mode handler is as easy as using INT 33h with the
appropriate interrupt number. But you probably will set the callbacks. In
which case, if you use INT 33h, it will just go to the protected mode IRQ
handler. And if this INT 33h call was from the protected mode IRQ handler,
well, lets just say infinite loop. If you have the callback set, and you wish
to chain (why bother?), you must make the address of the real mode IRQ handler
available to a real mode far routine that will call that handler as an
interrupt routine. You should call this real mode routine with INT 32h from
protected mode to do the IRQ chaining.
3.3 - The IF flag:
------------------
DPMI may need to virtualize the real system interrupt flag in protected
mode. For this reason you may not assume anything about the IF flag and
instructions which usually modify it. For example, PUSHF(D) may not store the
actual IF flag on the stack, and POPF(D) may not change either the real IF
flag or the virtual one. You can however be sure that CLI and STI will carry
out their functions correctly. Also, PMODE will replicate DPMIs native
interrupt flag functions if DPMI is not present. They are on INT 31h in
protected mode and are as follows:
AX=900h: Get state of IF then disable it. Returns AL set to the IF flag.
AX=901h: Get state of IF then enable it. Returns AL set to the IF flag.
AX=902h: Only returns AL set to the IF flag (0=disabled, 1=enabled).
3.4 - Exceptions:
-----------------
Under DPMI, exceptions are handled by the DPMI driver, which will usually
just terminate your program. Otherwise PMODE will automatically exit to DOS
on any exception not overridden by the low 8 IRQs. Any exception that is, will
erroneously be sent to that IRQ handler, be it a protected mode handler or
a real mode handler. Hey, you should not be getting exceptions in the first
place.
------------------------------------------------------------------------------
4 - Functions and data:
-----------------------
PMODE makes some variables available to your program concerning the state of
the system and memory. It also provides some functions for dealing with IRQs,
memory, and selectors. These functions and vars are defined in PMODE.INC. Just
include that somewhere in your code. Some of the functions are actually
defined as dwords, not as near lables. This is because their address may be
modified at startup to point to appropriate functions for the system type.
Don't worry about this. Just call them as you would call normal functions. So
here is a summary of all data and functions you can use. The functions
definitions are pretty self-explanatory. Except maybe that CF=0 means carry
flag clear, and CF=1 means a cheeseburger, hold the mayo.
4.0 - Variables:
----------------
_selcode:word - The 32bit code selector.
_seldata:word - The 32bit data selector (alias for the code).
_selzero:word - The 32bit data selector starting at absolute 0.
_lomembase:dword - The linear address of the current base of low memory.
_lomemtop:dword - The linear address of the current top of low memory.
_himembase:dword - The linear address of the current base of high memory.
_himemtop:dword - The linear address of the current top of high memory.
_pspa:dword - The linear address of the PSP from absolute 0.
_code16a:dword - The linear address of the CODE16 segment from absolute 0.
_code32a:dword - The linear address of the CODE32 segment from absolute 0.
_sysbyte0:byte - The low 2 bits are the system type, 0=raw, 1=XMS, 2=VCPI,
3=DPMI. The high 6 bits are undefined.
_exitcode:byte - The exit code you want to pass to DOS, 0 by default.
Here are the virtual registers for cross-mode calls:
v86r_eax:dword, v86r_ebx:dword, v86r_ecx:dword, v86r_edx:dword
v86r_esi:dword, v86r_edi:dword, v86r_ebp:dword
v86r_ah:byte, v86r_al:byte, v86r_bh:byte, v86r_bl:byte
v86r_ch:byte, v86r_cl:byte, v86r_dh:byte, v86r_dl:byte
v86r_ax:word, v86r_bx:word, v86r_cx:word, v86r_dx:word
v86r_si:word, v86r_di:word, v86r_bp:word
v86r_ds:word, v86r_es:word, v86r_fs:word, v86r_gs:word
4.1 - Memory functions:
-----------------------
_getlomem - Allocate some low mem
In:
EAX - size requested
Out:
CF=0 - memory allocated
CF=1 - not enough mem
EAX - linear pointer to mem or ?
_gethimem - Allocate some high mem
In:
EAX - size requested
Out:
CF=0 - memory allocated
CF=1 - not enough mem
EAX - linear pointer to mem or ?
_getmem - Allocate any mem, (first cheks low, then high)
In:
EAX - size requested
Out:
CF=0 - memory allocated
CF=1 - not enough mem
EAX - linear pointer to mem or ?
_lomemsize - Get amount of free low mem
Out:
EAX - number of bytes free
_himemsize - Get amount of free high mem
Out:
EAX - number of bytes free
4.2 - IRQ functions:
--------------------
_getirqvect - Get protected mode IRQ handler offset
In:
BL - IRQ num (0-0fh)
Out:
EDX - offset of IRQ handler
_setirqvect - Set protected mode IRQ handler offset
In:
BL - IRQ num (0-0fh)
EDX - offset of IRQ handler
_getirqmask - Get status of IRQ mask bit
In:
BL - IRQ num (0-15)
Out:
AL - status: 0=enabled, 1=disabled
_setirqmask - Set status of IRQ mask bit
In:
BL - IRQ num (0-15)
AL - status: 0=enabled, 1=disabled
_rmpmirqset - Set a real mode IRQ vect to redirect to pmode
In:
BL - IRQ number
EDX - offset of IRQ handler
EDI -> 21 byte buffer for code stub created
Out:
EAX - old seg:off of real mode IRQ handler
_rmpmirqfree - Reset a real more IRQ vect back to normal
In:
BL - IRQ number
EAX - seg:off of real mode IRQ handler
4.3 - Selector functions:
-------------------------
_getselector - Allocate a selector
Out:
CF=1 - selector not allocated
CF=0 - selector allocated
AX - 4G data selector or ?
_freeselector - Free an allocated selector
In:
AX - selector
_setselector - Set the base addx for a selector
In:
AX - selector
EDX - linear base addx for selector
4.4 - Other functions:
----------------------
_exit - Exit to real mode
_ret - Absolutely nothing, just a RET instruction
------------------------------------------------------------------------------
5 - Notes:
----------
In this section is a whole bunch of info that didn't really fit into the
other sections, or that I want to emphasize. If you run into a weird bug,
check here to for some possibilities. It is easy to forget enable interrupts
when your program gains control. Or to forget theres a limit to the stack
since control of it is taken away from your program.
5.0 - Updates (since 2.5):
--------------------------
PMODE 2.51:
) Added control of the order of DPMI/VCPI detection.
) Added compensation for EMM386 screwing the high bits of ESP in real mode.
5.1 - DMA Problems:
-------------------
As you know, the DMA controllers in the PC use all physical addresses.
Nothing but the processor itself knows how linear memory is arranged in the
physical memory banks. When paging is disabled, the relationship is very
simple. The linear address is always the same as the physical address. But
when you enable paging, that could get all screwed up. In raw mode and XMS,
you don't have to worry about this since paging is disabled. But under VCPI
and DPMI things are different. You can almost definately count on extended
memory addresses not being consistent with their physical addresses. Low
memory however, will usually map perfectly to its physical addresses. Unless
the program is running in some sort of multitasking system. Then the chances
are slim. The point is that you can't trust DMA much under VCPI and DPMI.
Ive seen the specs for VDS, and its useless for the type of DMA I need to
do. So there is no support for it in PMODE. If you feel like it, use it
yourself if you detect it.
5.2 - Misc notes:
-----------------
) No, you can't link PMODE with any high level languages.
) When linking, PMODE must be the first object in the link list.
) PMODE functions and INTs expect the direction flag to be clear.
) In IRQ handlers, you should not assume anything about the stack.
) Under VCPI, PMODE will allocate a maximum of 60M extended memory.
) You do not have to free any selectors you allocate before exiting.
) Remember to use 'IRETD' not 'IRET' at the end of protected mode IRQs.
) I would REALLY suggest not ever switching your stack in protected mode
yourself.
) Remember that upon reaching '_main', interrupts are still disabled. Don't
forget to do the STI.
) You can't write to memory with a CS: override in protected mode, but you can
read with a CS: override.
) This thing was tested under TASM 4.0. So if you have something different,
don't blame me if it doesnt compile.
) Yeah, theres no debugger. Quit whining, don't you realize it runs under
DPMI? So you can use any old DPMI debugger.
) When doing multiple nested cross-mode calls, keep in mind that the same
virtual regs are used to pass register values.
) Division faults, single step interrupts, the NMI, INT3, INTO, and BOUND
faults are sent to real mode handlers for the appropriate int number.
) If you modify the base address of a selector you allocated, make sure to
reload any segment registers containing that selector before using them.
) I hope you realize that in PMODE IRQ handlers, you don't have the BIOS to
redirect IRQ9 to IRQ2. So any device that uses IRQ2 will actually be using 9.
) If you're getting problems that smell of stack trouble, try increasing the
stack size vars at the top of PMODE.ASM and the maximum nesting level if
you're doing many calls across modes.
) Upon entry to a protected mode IRQ handler, remember that all segregs are
in an unknown state, including DS. Don't forget the 'mov ds,cs:_seldata'. Also
ofcourse preserve the previous value of all the regs.
) Remember that the INT31 AX=9?? flag functions are only available in pmode.
Go ahead, use the PUSHFs and POPFs in real mode to alter the IF flag... And
any DPMI host that can't handle that properly deserves to crash.
) There may be weird problems under some DPMIs. Many DPMI drivers out there
are anywhere from a little to extremely buggy. If you suspect your DPMI
driver, try running without it, or under a different DPMI driver.
) If you're gonna add other segments, put them between CODE16 and CODE32 only
if theyre small enough to still allow access to CODE32 data from CODE16.
Otherwise put them between CODE32 and CODEEND. You can also just stick your
16bit code in the CODE16 segment.
) Before exiting your program, you do not need to restore any protected mode
IRQ vectors. If you modified the real mode vector table, you gotta restore
those. And you do not have to restore the IRQ masks at 21h and A1h, PMODE
stores them before jumping to _main, and restores them before exiting.
) PMODE does not handle the VDISK low to high extended memory allocation
scheme because it is just plain stupid (and does not seem to be used outside
of XMS where its not a problem since the XMS driver is used to allocate mem).
If you wanna take precautions for this type of allocation, make yourself a
little macro to check for it and adjust _himembase accordingly.
5.3 - Final word:
-----------------
Well thats that... If you have a problem with something in PMODE, code your
own. Use this code, or don't, its your choice. Or just use it to learn if you
wish. Oh well...
L8r...
Tran...